package com.linkgie.galaxyframework.crypto;

import com.linkgie.galaxyframework.utils.Assert;

/**
 * 区块链地址生成器；
 * <p>
 * 实现了区块链地址生成算法；
 * <p>
 * 
 * 注：本实现的算法与比特币地址生成算法一致，使用的 Base58Check Encoding 前缀也与比特币地址前缀一致，为 0x00
 * ，目的是为了技术上的广泛兼容性；
 * 
 * @author spring
 *
 */
final class BlockchainAddressGenerator implements PublicKeyAddressGenerator {

	/**
	 * 区块链地址编码前缀；
	 * <p>
	 * 
	 * 采用与比特币地址的 Base58Check 编码前缀 0x00 值相等，但扩展为 32 位，目的是作为全局ID使用时可表达更多类型；
	 */
	public static final int BLOCKCHAIN_ADDRESS_PREFIX = 0;

	private static final String ALGORITHM_PREFIX = "PK.BC";

	private final int PREFIX;

	private HashFunc contentHasher;

	private HashFunc addressHasher;

	private Base58ChecksumEncoding encoding;

	private final String algorithm;

	@Override
	public String getAlogrithm() {
		return algorithm;
	}
	
	/**
	 * 区块链地址生成器；
	 * 
	 * @param contentHasher  针对输入内容进行前值计算的哈希函数；
	 * @param addressHasher  针对地址生成的哈希函数；
	 * @param encodingPrefix 地址编码的前缀；采用 Base58Checksum ；
	 * @param checksumHasher Base58Checksum 地址编码器采用的校验和 hash 函数；
	 */
	BlockchainAddressGenerator(HashService contentHasher, HashService addressHasher) {
		this(contentHasher, addressHasher, BLOCKCHAIN_ADDRESS_PREFIX, contentHasher);
	}

	/**
	 * 区块链地址生成器；
	 * 
	 * @param contentHasher  针对输入内容进行前值计算的哈希函数；
	 * @param addressHasher  针对地址生成的哈希函数；
	 * @param encodingPrefix 地址编码的前缀；采用 Base58Checksum ；
	 * @param checksumHasher Base58Checksum 地址编码器采用的校验和 hash 函数；
	 */
	BlockchainAddressGenerator(HashService contentHasher, HashService addressHasher, int prefixValue,
	HashService checksumHasher) {
		this.PREFIX = prefixValue;
		this.contentHasher = contentHasher;
		this.addressHasher = addressHasher;
		this.encoding = new Base58ChecksumEncoding(checksumHasher);

		this.algorithm = HashAddressAlgorithms.createName(ALGORITHM_PREFIX, contentHasher.getAlgorithm(), addressHasher.getAlgorithm());

	}
	
	/**
	 * 采用比特币地址前缀；
	 * 
	 * @param contentHasher  针对输入内容进行前值计算的哈希函数；
	 * @param addressHasher  针对地址生成的哈希函数；
	 * @return
	 */
	public static BlockchainAddressGenerator createStandardGenerator(HashService contentHasher, HashService addressHasher) {
		return new BlockchainAddressGenerator(contentHasher, addressHasher, BLOCKCHAIN_ADDRESS_PREFIX, contentHasher);
	}

	/**
	 * 生成全局唯一ID；
	 * <p>
	 */
	@Override
	public PublicKeyAddress generate(PublicKey publicKey) {
		Assert.notNull(publicKey, "Public key is null");
		// 采用平台无关、实现无关的原始的公钥数据进行编码，以实现 UUID 的跨平台通用性；
		byte[] rawAddress = addressHasher.hashInRaw(contentHasher.hashInRaw(publicKey.getRaw()));

		String encodedAddress = encoding.encode(PREFIX, rawAddress);

		return new PublicKeyAddressEntry(algorithm, publicKey, rawAddress, encodedAddress);
	}

//	@Override
//	public byte[] generateUUIDInRaw(PublicKey publicKey) {
//		// 采用平台无关、实现无关的原始的公钥数据进行编码，以实现 UUID 的跨平台通用性；
//		byte[] uuid = uuidHasher.hashInRaw(firstHasher.hashInRaw(publicKey.getRaw()));
//		return uuid;
//	}
//
//	@Override
//	public String generateUUIDInEncoded(PublicKey publicKey) {
//		// 采用平台无关、实现无关的原始的公钥数据进行编码，以实现 UUID 的跨平台通用性；
//		byte[] uuid = uuidHasher.hashInRaw(firstHasher.hashInRaw(publicKey.getRaw()));
//
//		String encodedUUID = encoding.encode(ENCODING_PREFIX, uuid);
//		return encodedUUID;
//	}

	private static class PublicKeyAddressEntry implements PublicKeyAddress {

		private String algorithm;

		private byte[] address;

		private String encodedAddress;

		private PublicKey publicKey;

//		private int encodingPrefix;

		private PublicKeyAddressEntry(String algorithm, PublicKey publicKey, byte[] address, String encodedAdress) {
			this.algorithm = algorithm;
			this.publicKey = publicKey;
			this.address = address;
//			this.encodingPrefix = encodingPrefix;
			this.encodedAddress = encodedAdress;
		}


		@Override
		public String getAlgorithm() {
			return algorithm;
		}

		@Override
		public PublicKey getPublicKey() {
			return publicKey;
		}

		@Override
		public byte[] toBytes() {
			return address.clone();
		}

		@Override
		public String toBase58Checksum() {
			return encodedAddress;
		}

//		@Override
//		public int getEncodingPrefix() {
//			return encodingPrefix;
//		}

	}

}